package cn.pli.bike.utils;

import android.bluetooth.BluetoothProfile;
import android.os.Message;
import android.support.annotation.NonNull;

import com.framemodule.utils.CommonUtils;
import com.framemodule.utils.Logger;
import com.inuker.bluetooth.library.BluetoothClient;
import com.inuker.bluetooth.library.Constants;
import com.inuker.bluetooth.library.connect.listener.BleConnectStatusListener;
import com.inuker.bluetooth.library.connect.options.BleConnectOptions;
import com.inuker.bluetooth.library.connect.response.BleConnectResponse;
import com.inuker.bluetooth.library.connect.response.BleNotifyResponse;
import com.inuker.bluetooth.library.connect.response.BleWriteResponse;
import com.inuker.bluetooth.library.model.BleGattProfile;
import com.lzh.des_lib.DesUtil;
import com.nostra13.universalimageloader.utils.L;


import org.greenrobot.eventbus.EventBus;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import cn.pli.bike.bean.BtStatus;
import cn.pli.bike.bean.NoticeBean;
import okhttp3.internal.Internal;
import rx.Observable;
import rx.Subscriber;
import rx.functions.Action0;
import rx.functions.Func1;
import rx.functions.Func2;

public class ControlBlueUtils {

    private static final byte[] CIPHER = {0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39};

    //获取传递的蓝牙
    BluetoothClient mBtClient;
    private String mAddress;

    public ControlBlueUtils(BluetoothClient mBtClient, String mAddress, String cipher) {
        this.mBtClient = mBtClient;
        this.mAddress = mAddress;
        initCipher(cipher);

        //  test1();

    }


    private void initCipher(String cipher) {
        if (CommonUtils.isEmpty(cipher))
            return;
        for (int i = 0; i < cipher.length(); i++) {
            String number = cipher.substring(i, i + 1);
            int index = Integer.parseInt(number);
            COM_LOGIN[2 + i] = CIPHER[index];
        }
    }

    //蓝牙连接状态监听
    private BleConnectStatusListener mBleConnectStatusListener = new BleConnectStatusListener() {
        @Override
        public void onConnectStatusChanged(String mac, int status) {
            if (status == Constants.STATUS_CONNECTED) {
                showStatus("成功连接！");
                // login();

            } else if (status == Constants.STATUS_DISCONNECTED) {
                showStatus("断开连接！");

            }
        }
    };

    //两把锁
    private final Object commandMonitor = new Object();
    private final Object configMonitor = new Object();
    //两个蓝牙通知监听集合
    private Map<String, BleNotifyResponse> commandListeners = Collections.synchronizedMap(new HashMap<String, BleNotifyResponse>());
    private Map<String, BleNotifyResponse> configListeners = Collections.synchronizedMap(new HashMap<String, BleNotifyResponse>());

    //蓝牙通知监听
    private final BleNotifyResponse mTxNotifyResponse = new BleNotifyResponse() {
        @Override
        public void onNotify(UUID service, UUID character, byte[] value) {
            showStatus("收到TX通道通知 character = " + character + "  value = " + toHexString(value));
            if (value[2] == 0x00 && value[4] == 0x11) {//应答命令
                onlylogin();

            }
            if (value[2] == 0x12) {
                closeSuccess();

            }


            if (!BluetoothUUID.CONFIG.equals(character)) {
                synchronized (commandMonitor) {
                    Iterator<Map.Entry<String, BleNotifyResponse>> iterator = commandListeners.entrySet().iterator();
                    if (iterator.hasNext()) {
                        Map.Entry<String, BleNotifyResponse> entry = iterator.next();
                        entry.getValue().onNotify(service, character, value);
                    }
                }
            } else {
                synchronized (configMonitor) {
                    Iterator<Map.Entry<String, BleNotifyResponse>> iterator = configListeners.entrySet().iterator();
                    if (iterator.hasNext()) {
                        Map.Entry<String, BleNotifyResponse> entry = iterator.next();
                        entry.getValue().onNotify(service, character, value);
                    }
                }
            }
        }

        @Override
        public void onResponse(int code) {
            //showStatus("mTxNotifyResponse  onResponse.code = "+code);
        }
    };

    private final BleNotifyResponse mConfigNotifyResponse = new BleNotifyResponse() {
        @Override
        public void onNotify(UUID service, UUID character, byte[] value) {
            showStatus("收到Config通道通知 character = " + character + "  value = " + toHexString(value));
            synchronized (configMonitor) {
                Iterator<Map.Entry<String, BleNotifyResponse>> iterator = configListeners.entrySet().iterator();
                if (iterator.hasNext()) {
                    Map.Entry<String, BleNotifyResponse> entry = iterator.next();
                    entry.getValue().onNotify(service, character, value);
                }
            }
        }

        @Override
        public void onResponse(int code) {
            if (code == Constants.REQUEST_SUCCESS) {
                login();
            }
        }
    };


    //数据集
    private List<BtStatus> mStatusItems = new ArrayList<>();

    //命令
    //登陆
    private static byte[] COM_LOGIN = {0x08, 0x00, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31};
    //获取随机数
    private static final byte[] COM_GET_CODE = {(byte) 0xFA, 0x01, 0x02, 0x01, 0x00, 0x02, (byte) 0xFD};
    //关锁
    private static final byte[] COM_LOCK = {(byte) 0xFA, 0x01, 0x12, 0x01, 0x00, 0x12, (byte) 0xFD};
    //获取状态
    private static final byte[] COM_GET_STATUS = {(byte) 0xFA, 0x01, 0x01, 0x01, 0x00, 0x01, (byte) 0xFD};

    //命令类型
    private static final int TYPE_COM_CONFIG = 0;
    private static final int TYPE_COM_NORMAL = 1;

    //重试常量
    private static final long TIMEOUT = 1000;
    private static final int RETRY_COUNT = 3;
    private static final byte[] NO_RESPONSE = new byte[0];


    /**
     * 连接蓝牙
     */
    public void connect() {
        // disconnect();

        BleConnectOptions options = new BleConnectOptions.Builder()
                .setConnectRetry(3)
                .setConnectTimeout(30000)
                .setServiceDiscoverRetry(3)
                .setServiceDiscoverTimeout(20000)
                .build();

        //注册连接状态监听
        mBtClient.registerConnectStatusListener(mAddress, mBleConnectStatusListener);

        mBtClient.connect(mAddress, options, new BleConnectResponse() {
            @Override
            public void onResponse(int code, BleGattProfile data) {
                if (code == Constants.REQUEST_SUCCESS) {

                    mBtClient.notify(mAddress, BluetoothUUID.SERVICE,
                            BluetoothUUID.CONFIG, mConfigNotifyResponse);

                    UUID[] notify = new UUID[]{
                            BluetoothUUID.NOTIFICATION_0, BluetoothUUID.NOTIFICATION_1,
                            BluetoothUUID.NOTIFICATION_2, BluetoothUUID.NOTIFICATION_3,
                            BluetoothUUID.NOTIFICATION_4, BluetoothUUID.NOTIFICATION_5,
                    };
                    for (UUID uuid : notify) {
                        mBtClient.notify(mAddress, BluetoothUUID.SERVICE,
                                uuid, mTxNotifyResponse);
                    }
                } else if (code == Constants.REQUEST_FAILED) {
                    showStatus("连接失败！");
                    openError();
                } else if (code == Constants.REQUEST_TIMEDOUT) {
                    showStatus("连接超时！");
                    openError();

                }
            }
        });
    }

    /**
     * 清除
     */


    /**
     * 关锁
     */
    private void lock() {
        writeCommand(COM_LOCK)
                .subscribe(new SimpleSubscriber<byte[]>() {
                    @Override
                    public void onNext(byte[] bytes) {
                        if (bytes[2] == 0x00) {//应答命令
                            if (bytes[5] == 0x00) {
                                showStatus("关锁成功");
                            } else {
                                showStatus("关锁失败");
                            }
                        } else {
                            //读取电量
                            showStatus("得到电量：" + (bytes[4] / (float) 0x64 * 100) + "%");
                            unsubscribe();
                        }
                    }
                });
    }

    /**
     * 开锁
     */
    private void unlocking() {
        writeCommand(COM_GET_CODE)
                .switchMap(new Func1<byte[], Observable<byte[]>>() {
                    @Override
                    public Observable<byte[]> call(byte[] bytes) {
                        byte[] random = Arrays.copyOfRange(bytes, 4, 12);
                        return writeCommand(getUnlockingCommand(random));
                    }
                })
                .subscribe(new SimpleSubscriber<byte[]>() {
                    @Override
                    public void onNext(@NonNull byte[] bytes) {
                        if (bytes[5] == 0x00) {
                            showStatus("开锁成功");
                            // openSuccess();
                        } else {
                            showStatus("开锁失败");
                        }
                        unsubscribe();
                    }
                });

    }

    /**
     * 开锁成功
     */
    private void openSuccess() {
        NoticeBean noticeBean = new NoticeBean("", NoticeBean.LOCK_OPEN);
        noticeBean.setLocktype(NoticeBean.LOCK_BLUE);
        Message message1 = new Message();
        message1.what = EventBusAction.PUSH_ACTION;
        message1.obj = noticeBean;
        EventBus.getDefault().post(message1);
    }

    /**
     * 开锁失败
     */
    private void openError() {

        Message message = new Message();
        message.what = EventBusAction.OPEN_LOCK_BLUE_ERROE;
        EventBus.getDefault().post(message);

    }

    private void closeSuccess() {
        Message message = new Message();
        message.what = EventBusAction.LOCK_BLUE_CLOSE_SECCESS;
        EventBus.getDefault().post(message);

    }


    /**
     * 蓝牙登录
     */
    private void login() {
        //写入配置指令
        writeConfig(COM_LOGIN)
                .filter(new Func1<byte[], Boolean>() {
                    @Override
                    public Boolean call(byte[] bytes) {
                        //写配置时，没有统一的方法根据通知判断写的什么类型的命令，故在此通过主动过滤得到想要的结果
                        return bytes[1] == 0x00;
                    }
                })
                .compose(retry(new Func1<byte[], Boolean>() {
                    @Override
                    public Boolean call(byte[] bytes) {
                        return bytes[2] != 0x01;//未登录成功
                    }
                }))
                .switchMap(new Func1<byte[], Observable<byte[]>>() {
                    @Override
                    public Observable<byte[]> call(byte[] bytes) {
                        showStatus("login-switchMap.call.bytes =" + toHexString(bytes));
                        if (bytes == NO_RESPONSE) {
                            showStatus("已经登陆成功");
                            return writeCommand(COM_GET_STATUS)
                                    .compose(retry(new Func1<byte[], Boolean>() {
                                        @Override
                                        public Boolean call(byte[] bytes) {
                                            return bytes.length != 18;
                                        }
                                    }));
                        } else if (bytes[2] == 0x01) {
                            showStatus("登陆成功");
                            return writeCommand(COM_GET_STATUS)
                                    .compose(retry(new Func1<byte[], Boolean>() {
                                        @Override
                                        public Boolean call(byte[] bytes) {
                                            return bytes.length != 18;
                                        }
                                    }));
                        }
                        return Observable.error(new RuntimeException("登陆失败"));
                    }
                })
                .subscribe(new SimpleSubscriber<byte[]>() {
                    @Override
                    public void onNext(byte[] bytes) {
                        if (bytes == NO_RESPONSE) {
                            showStatus("无法登陆蓝牙");
                        } else if (bytes.length != 18) {
                            showStatus("读取状态错误");
                        } else {
                            String content = "锁状态: ";
                            if (bytes[4] == 0x0b) {
                                content += "开锁";
                            } else if (bytes[4] == 0x0f) {
                                content += "关锁";
                            } else {
                                content += "未知";
                            }
                            content += "\n电量：" + (bytes[5] / (float) 0x64 * 100) + "%"
                                    + "\n版本号：" + new String(Arrays.copyOfRange(bytes, 6, 16));
                            showStatus(content);
                            unlocking();
                        }

                        unsubscribe();
                    }

                    @Override
                    public void onError(Throwable e) {
                        showStatus(e.getMessage());
                    }
                });

    }


    /**
     * 蓝牙登录
     */
    private void onlylogin() {
        //写入配置指令
        writeConfig(COM_LOGIN)
                .filter(new Func1<byte[], Boolean>() {
                    @Override
                    public Boolean call(byte[] bytes) {
                        //写配置时，没有统一的方法根据通知判断写的什么类型的命令，故在此通过主动过滤得到想要的结果
                        return bytes[1] == 0x00;
                    }
                })
                .compose(retry(new Func1<byte[], Boolean>() {
                    @Override
                    public Boolean call(byte[] bytes) {
                        return bytes[2] != 0x01;//未登录成功
                    }
                }))
                .switchMap(new Func1<byte[], Observable<byte[]>>() {
                    @Override
                    public Observable<byte[]> call(byte[] bytes) {
                        showStatus("login-switchMap.call.bytes =" + toHexString(bytes));
                        if (bytes == NO_RESPONSE) {
                            showStatus("已经登陆成功");
                            return writeCommand(COM_GET_STATUS)
                                    .compose(retry(new Func1<byte[], Boolean>() {
                                        @Override
                                        public Boolean call(byte[] bytes) {
                                            return bytes.length != 18;
                                        }
                                    }));
                        } else if (bytes[2] == 0x01) {
                            showStatus("登陆成功");
                            return writeCommand(COM_GET_STATUS)
                                    .compose(retry(new Func1<byte[], Boolean>() {
                                        @Override
                                        public Boolean call(byte[] bytes) {
                                            return bytes.length != 18;
                                        }
                                    }));
                        }
                        return Observable.error(new RuntimeException("登陆失败"));
                    }
                })
                .subscribe(new SimpleSubscriber<byte[]>() {
                    @Override
                    public void onNext(byte[] bytes) {
                        if (bytes == NO_RESPONSE) {
                            showStatus("无法登陆蓝牙");
                        } else if (bytes.length != 18) {
                            showStatus("读取状态错误");
                        } else {
                            String content = "锁状态: ";
                            if (bytes[4] == 0x0b) {
                                content += "开锁";
                                openSuccess();
                            } else if (bytes[4] == 0x0f) {
                                openError();
                                content += "关锁";
                            } else {
                                openError();
                                content += "未知";
                            }
                            content += "\n电量：" + (bytes[5] / (float) 0x64 * 100) + "%"
                                    + "\n版本号：" + new String(Arrays.copyOfRange(bytes, 6, 16));
                            showStatus(content);
                            //unlocking();
                        }

                        unsubscribe();
                    }

                    @Override
                    public void onError(Throwable e) {
                        showStatus(e.getMessage());
                    }
                });

    }


    /**
     * 写配置
     */
    private Observable<byte[]> writeConfig(final byte[] command) {

        final String key = UUID.randomUUID().toString();


        return Observable.create(new Observable.OnSubscribe<byte[]>() {
            @Override
            public void call(final Subscriber<? super byte[]> subscriber) {
                //验证命令
                if (!verifyCommand(TYPE_COM_CONFIG, command)) {
                    subscriber.onError(new IllegalArgumentException("Illegal Command: " + toHexString(command)));
                    return;
                }

                //建立当前命令的监听
                BleNotifyResponse listener = new BleNotifyResponse() {
                    @Override
                    public void onNotify(UUID service, UUID character, byte[] value) {
                        showStatus("config-notify: " + toHexString(value));
                        subscriber.onNext(value);
                    }

                    @Override
                    public void onResponse(int code) {
                    }
                };

                //加入监听集合
                synchronized (configMonitor) {
                    configListeners.put(key, listener);
                }
            }
        }).doOnSubscribe(new Action0() {
            @Override
            public void call() {
                //写数据到蓝牙
                mBtClient.write(mAddress, BluetoothUUID.SERVICE,
                        BluetoothUUID.CONFIG, command, new BleWriteResponse() {
                            @Override
                            public void onResponse(int code) {
                                //记录状态
//                                codeHolder.code = code;

                                if (code == Constants.REQUEST_SUCCESS) {
                                    showStatus("writeConfig写数据到特征成功" + toHexString(command));
                                } else {
                                    showStatus("writeConfig写数据到特征失败" + toHexString(command));
                                }
                            }
                        });
            }
        }).doOnUnsubscribe(new Action0() {
            @Override
            public void call() {
                synchronized (configMonitor) {
                    configListeners.remove(key);
                }
            }
        });
    }

    /**
     * 写命令
     */
    private Observable<byte[]> writeCommand(final byte[] command) {
        final String key = UUID.randomUUID().toString();

//        final CodeHolder codeHolder = new CodeHolder();

        return Observable.create(new Observable.OnSubscribe<byte[]>() {
            @Override
            public void call(final Subscriber<? super byte[]> subscriber) {
                if (!verifyCommand(TYPE_COM_NORMAL, command)) {
                    subscriber.onError(new IllegalArgumentException("Illegal Command: " + toHexString(command)));
                    return;
                }

                //创建监听
                BleNotifyResponse listener = new BleNotifyResponse() {
                    @Override
                    public void onNotify(UUID service, UUID character, byte[] value) {
                        showStatus("command-notify: " + toHexString(value));
//                        if (codeHolder.code == Constants.REQUEST_SUCCESS) {
                        if (value[2] == 0x00 && value[4] == command[2]) {//应答命令
                            subscriber.onNext(value);
                        } else if (value[2] == command[2]) {//从机返回对应数据
                            subscriber.onNext(value);
                        }
//                        }
                    }

                    @Override
                    public void onResponse(int code) {
                    }
                };

                //加入集合
                synchronized (commandMonitor) {
                    commandListeners.put(key, listener);
                }

            }

            ;
        })
                .doOnSubscribe(new Action0() {
                    @Override
                    public void call() {
                        mBtClient.write(mAddress, BluetoothUUID.SERVICE,
                                BluetoothUUID.WRITE, command, new BleWriteResponse() {
                                    @Override
                                    public void onResponse(int code) {
//                                  codeHolder.code = code;
                                        if (code == Constants.REQUEST_SUCCESS) {
                                            showStatus("writeCommand写数据到特征成功" + toHexString(command));
                                        } else {
                                            showStatus("writeCommand写数据到特征失败" + toHexString(command));
                                        }

                                    }
                                });
                    }
                })
                .doOnUnsubscribe(new Action0() {
                    @Override
                    public void call() {
                        synchronized (commandMonitor) {
                            commandListeners.remove(key);
                        }
                    }
                });
    }

    /**
     * 重试
     *
     * @param needRetry
     * @return
     */
    private static Observable.Transformer<byte[], byte[]> retry(final Func1<byte[], Boolean> needRetry) {
        return retry(TIMEOUT, RETRY_COUNT, NO_RESPONSE, needRetry);
    }

    /**
     * 重试逻辑
     *
     * @param timeoutMs   超时时间 毫秒
     * @param retryCount  重试次数
     * @param defaultData 没有数据返回时的默认数据
     * @param needRetry   逻辑：什么情况下重试，返回 true 进行重试， 返回 false 或 null 不重试
     * @param <T>         类型
     * @return
     */
    private static <T> Observable.Transformer<T, T> retry(
            final long timeoutMs,
            final int retryCount,
            final T defaultData,
            final Func1<T, Boolean> needRetry
    ) {

        return new Observable.Transformer<T, T>() {
            T lastData;

            @Override
            public Observable<T> call(Observable<T> observable) {
                if ((timeoutMs <= 0 && needRetry == null) || retryCount <= 0) return observable;
                if (timeoutMs > 0) {
                    observable = observable.timeout(timeoutMs, TimeUnit.MILLISECONDS);
                }
                if (needRetry != null) {
                    observable = observable.flatMap(new Func1<T, Observable<T>>() {
                        @Override
                        public Observable<T> call(T data) {
                            lastData = data;
                            Boolean result = true;
                            try {
                                result = needRetry.call(data);
                            } catch (Exception e) {
                                //
                            }
                            if (result != null && result) {
                                return Observable.error(new Exception("retry"));
                            }
                            return Observable.just(data);
                        }
                    });
                }
                return observable.retryWhen(new Func1<Observable<? extends Throwable>, Observable<?>>() {
                    @Override
                    public Observable<?> call(Observable<? extends Throwable> observable) {
                        return observable.zipWith(Observable.range(0, retryCount), new Func2<Throwable, Integer, Integer>() {
                            @Override
                            public Integer call(Throwable throwable, Integer integer) {
                                return integer;
                            }
                        });
                    }
                }).defaultIfEmpty(lastData == null ? defaultData : lastData);
            }
        };
    }


    /**
     * 显示蓝牙状态
     *
     * @param content
     */
    private void showStatus(String content) {
        Logger.d(content);
        mStatusItems.add(new BtStatus(System.currentTimeMillis(), content));
    }

    /**
     * 验证蓝牙命令
     *
     * @return
     */
    private boolean verifyCommand(int type, byte[] command) {
        if (type == TYPE_COM_NORMAL) {
            return !(command == null
                    || command.length < 7
                    || command[0] != (byte) 0xfa
                    || command[1] != 0x01
                    || command[command.length - 1] != (byte) 0xfd);
        } else if (type == TYPE_COM_CONFIG) {
            return true;
        }
        return false;
    }

    /**
     * 转成16进制
     */
    private String toHexString(byte[] data) {
        StringBuilder builder = new StringBuilder("{");
        if (data == null) {
            builder.append("null");
        } else {
            for (byte b : data)
                builder.append(String.format(Locale.CANADA, "%02x", b & 0xff)).append(", ");
        }
        builder.append("}");
        return builder.toString();
    }

    /**
     * 转成16进制
     */
    private String toHexString(byte data) {
        StringBuilder builder = new StringBuilder("{");

        builder.append(String.format(Locale.CANADA, "%02x", data & 0xff)).append(", ");

        builder.append("}");
        return builder.toString();
    }

    //获取解锁命令
    private byte[] getUnlockingCommand(byte[] randomData) {
        byte[] tmp = {0x01, 0x11, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
        memcpy(tmp, DesUtil.decryXor(randomData), 3);
        Logger.d("foxytmp", toHexString(tmp));
        byte xm = DesUtil.xorSum(tmp);
        Logger.d("foxyxm", toHexString(xm));
        byte[] com = {(byte) 0xFA, 0x01, 0x11, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xFD};
        memcpy(com, tmp, 1);
        com[13] = xm;
        Logger.d("foxycom", toHexString(com));
        return com;
    }

    private void memcpy(byte[] dest, byte[] src, int start) {
        if (dest == null || src == null || start < 0 || start > dest.length) return;
        for (int i = start, j = 0; i < dest.length && j < src.length; ++i, ++j) {
            dest[i] = src[j];
        }
    }


    //记录写入蓝牙指令返回的状态
//    private static class CodeHolder {
//        int code = 100;
//    }

    //断开连接
    private void disconnect() {

        if (mBtClient != null)
            mBtClient.unregisterConnectStatusListener(mAddress, mBleConnectStatusListener);

        if (mBtClient.getConnectStatus(mAddress) == BluetoothProfile.STATE_CONNECTED)
            mBtClient.disconnect(mAddress);

        synchronized (configListeners) {
            configListeners.clear();
        }

        synchronized (commandListeners) {
            commandListeners.clear();
        }
    }

    public void test1() {
        byte[] bytes = {(byte) 0xFB, 0x01, 0x02, 0x08, 0x38, (byte) 0xF7, 0x18, (byte) 0x84, 0x79, 0x44, 0x74, 0x42, 0x53, (byte) 0xFD};
        byte[] random = Arrays.copyOfRange(bytes, 4, 12);
        Logger.d("random", toHexString(random));

        getUnlockingCommand(random);

    }
}
